package com.excilys.service;

import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.excilys.dao.ITransactionDAO;
import com.excilys.model.Account;
import com.excilys.model.IllegalAccesException;
import com.excilys.model.Transaction;
import com.excilys.model.TransactionCardCumul;
import com.excilys.model.TransactionTransfer;
import com.excilys.model.TransactionType;
import com.excilys.utils.Constants;

/**
 * Class that implements services related to users.
 * 
 * @author excilys dev : SC, YA, VP
 * 
 */

@Service
@Transactional(readOnly = true)
public class TransactionService {

	@Autowired
	private ITransactionDAO myServiceDAO;

	public ITransactionDAO getMyServiceDAO() {
		return myServiceDAO;
	}

	public void setMyServiceDAO(ITransactionDAO myServiceDAO) {
		this.myServiceDAO = myServiceDAO;
	}

	/**
	 * Method that takes a transaction list (and an account) and returns a list where the card transactions are compiled in 1 single object (or row)
	 * 
	 * @param transactionList
	 * @param transactionAccount
	 * @return
	 * @throws UnauthorizedDateException
	 */
	public Transaction refactorCardTransactions(List<Transaction> transactionList, Account transactionAccount, DateTime selectedDate) {
		checkAutorizedDate(selectedDate);

		Transaction onlyCardTransaction = new TransactionCardCumul(0, selectedDate, transactionAccount, "Accumulated Card Transactions");
		TransactionType cardTransactionType = TransactionType.CARD;

		for (Transaction singleTransaction : transactionList) {
			if (singleTransaction.getTransactionType() == cardTransactionType) {
				onlyCardTransaction.setTransactionAmount(onlyCardTransaction.getTransactionAmount() + singleTransaction.getTransactionAmount());
			}
		}

		if (onlyCardTransaction.getTransactionAmount() == 0.0)
			onlyCardTransaction = null;

		return onlyCardTransaction;
	}

	/**
	 * Used in accountDetailsController for account details. Retrieves the list of all transactions by date.
	 * 
	 * @param accountId
	 * @param currentDate
	 * @return a list of transactions
	 * @throws UnauthorizedDateException
	 */
	public List<Transaction> getAllTransactionsByAccountIdByMonthAndYear(long accountId, DateTime selectedDate) {
		checkAutorizedDate(selectedDate);
		return myServiceDAO.getAllTransactionsByAccountIdByMonthAndYear(accountId, selectedDate);
	}

	/**
	 * Used in accountDetailsController for account details. Retrieves the list of transfer and check transactions by date.
	 * 
	 * @param accountId
	 * @param currentDate
	 * @return a list of check and transfer transactions
	 * @throws UnauthorizedDateException
	 */
	public List<Transaction> getAllTransactionsCheckTransferByAccountIdByMonthAndYear(Long accountId, DateTime selectedDate) {
		checkAutorizedDate(selectedDate);
		return myServiceDAO.getAllTransactionsCheckTransferByAccountIdByMonthAndYear(accountId, selectedDate);
	}

	/**
	 * Used on accountDetailsController for account details. Retrieves the list of transfer and check transactions by date. The offset sets a result list of 20 elements.
	 * 
	 * Besides, it manages the inversion of amount and destination account.
	 * 
	 * @param accountId
	 * @param currentDate
	 * @param offset
	 * @return a list of 20 transfer and check transactions depending on page number
	 * @throws UnauthorizedDateException
	 */
	public List<Transaction> getAllTransactionsCheckTransferByAccountIdByMonthAndYear(long accountId, DateTime selectedDate, int offset) {
		checkAutorizedDate(selectedDate);

		return myServiceDAO.getAllTransactionsCheckTransferByAccountIdByMonthAndYear(accountId, selectedDate, offset);

	}

	public List<Transaction> refactorTransferTransactions(List<Transaction> transactionsList, Account transactionAccount, DateTime selectedDate) {
		checkAutorizedDate(selectedDate);

		for (Transaction oneTransaction : transactionsList) {
			if (oneTransaction.getTransactionType() == TransactionType.TRANSFER) {
				if (((TransactionTransfer) oneTransaction).getTransferAccount().getAccountId() == transactionAccount.getAccountId()) {
					oneTransaction.setTransactionAmount(-oneTransaction.getTransactionAmount());
					((TransactionTransfer) oneTransaction).setTransferAccount(oneTransaction.getTransactionAccount());
					oneTransaction.setTransactionAccount(((TransactionTransfer) oneTransaction).getTransferAccount());
				}
			}
		}

		return transactionsList;
	}

	/**
	 * Used in accountDetailsController for account details. Retrieves the list of card transactions by date.
	 * 
	 * @param accountId
	 * @param currentDate
	 * @return a list of card transactions
	 * @throws UnauthorizedDateException
	 */
	public List<Transaction> getAllTransactionsCardByAccountIdByMonthAndYear(Long accountId, DateTime selectedDate) {
		checkAutorizedDate(selectedDate);
		return myServiceDAO.getAllTransactionsCardByAccountIdByMonthAndYear(accountId, selectedDate);
	}

	/**
	 * Used in accountDetailsController for account details. Retrieves the list of card transactions by date. The offset sets a result list of 20 elements.
	 * 
	 * @param accountId
	 * @param currentDate
	 * @param offset
	 * @return a list of 20 card transactions depending on page number
	 * @throws UnauthorizedDateException
	 */
	public List<Transaction> getAllTransactionsCardByAccountIdByMonthAndYear(Long accountId, DateTime selectedDate, int offset) {
		checkAutorizedDate(selectedDate);
		return myServiceDAO.getAllTransactionsCardByAccountIdByMonthAndYear(accountId, selectedDate, offset);
	}

	/**
	 * Used by transfer form to perform a transfer between two accounts registered in transactionAccount_accounId (source account) and transferAccount_accountId (target account).
	 * Performs a plain persistence.
	 * 
	 * @param sourceAccountName
	 * @param targetAccountName
	 * @param transferAmount
	 */
	@Transactional(readOnly = false)
	public void setTransferTransaction(String sourceAccountName, String targetAccountName, double transferAmount) {
		myServiceDAO.setTransferTransaction(sourceAccountName, targetAccountName, transferAmount);
	}

	/**
	 * Check that the wanted date is not earlier than 6 month before currentDate
	 */
	private void checkAutorizedDate(DateTime selectedDate) {
		DateTime currentDate = new DateTime();
		currentDate = currentDate.minusMonths(6);
		currentDate = currentDate.withDayOfMonth(1);
		if (selectedDate.isBefore(currentDate.getMillis())) {
			throw new IllegalAccesException();
		}
	}

	/**
	 * Returns intPage from the current page number in URL and calculates the
	 * offset for query results of this same class methods.
	 * 
	 * @param intPage
	 * @return int offset
	 */
	public int getOffset(int intPage) {
		int offset = (intPage - 1) * Constants.MAX_RESULT;
		return offset;
	}

	/**
	 * Returns total number of pages of transactions. Used by
	 * accountCardDetailsController and accountDetailsController to display
	 * total number of (card) transaction(s) per month.
	 * 
	 * @param transactionsList
	 * @return int nbPages
	 */
	public int getNbPagesAccordingToTransactionListSize(List<Transaction> transactionsList) {
		int nbPages;
		if (transactionsList.size() <= 20) {
			nbPages = (transactionsList.size() / Constants.MAX_RESULT);
		} else {
			nbPages = (transactionsList.size() / Constants.MAX_RESULT) + 1;
		}
		return nbPages;
	}

	/**
	 * Returns a list of six last month from current date.
	 * 
	 * @param nowDate
	 * @return List<DateTime> dateLastSixMonth
	 */
	public List<DateTime> getDatelastSixMonthsList(DateTime nowDate) {
		List<DateTime> dateLastSixMonth = new ArrayList<DateTime>();
		for (int i = 0; i < 6; i++) {
			dateLastSixMonth.add(nowDate.minusMonths(i));
		}
		return dateLastSixMonth;
	}

}
